﻿Shader "Unity Shaders Book/Chapter 12/Gaussian Blur"
{
    Properties
    {
        // 主纹理
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _BlurSize ("Blur Size", Float) = 1.0
    }

    SubShader
    {
        // 定义可复用的代码块，可在不同Pass中调用，类似C++中头文件的功能
        CGINCLUDE

        #include "UnityCG.cginc"

        sampler2D _MainTex;
        // 主纹理的纹素大小（例如：一张512 * 512的纹理，纹素大小为1/512）
        // 利用纹素，做相邻区域内纹理采样时，计算各相邻区域的纹理坐标
        half4 _MainTex_TexelSize;
        float _BlurSize;

        struct v2f
        {
            float4 pos : SV_POSITION;
            half2 uv[5] : TEXCOORD0;
        };

        // 通过把计算采样纹理坐标的代码从片元着色器中转移到顶点着色器中，可以减少运算，提高性能
        // 由于从顶点着色器到片元着色器的差值是线性的，因此这样的转移并不会影响纹理坐标的计算结果
        v2f vertBlurVertical(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);

            // 构建一维的纵向高斯核
            half2 uv = v.texcoord;
            o.uv[0] = uv;
            // _BlurSize控制邻域像素之间的采样距离，_BlurSize值越大，模糊程度越高，但采样数不会受到影响
            // 但过大的_BlurSize值会造成虚影
            o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
            o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;

            return o;
        }

        v2f vertBlurHorizontal(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);

            // 构建一维的纵向高斯核
            half2 uv = v.texcoord;
            o.uv[0] = uv;
            // _BlurSize控制邻域像素之间的采样距离，_BlurSize值越大，模糊程度越高，但采样数不会受到影响
            // 但过大的_BlurSize值会造成虚影
            o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
            o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
            o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
            o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;

            return o;
        }

        fixed4 fragBlur(v2f i) : SV_Target
        {
            // 只需要记录3个高斯权重
            float weight[3] = {0.4026, 0.2442, 0.0545};
            // 权重和
            fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];

            for (int it = 1; it < 3; it++)
            {
                sum += tex2D(_MainTex, i.uv[it]).rgb * weight[it];
                sum += tex2D(_MainTex, i.uv[2 * it]).rgb * weight[it];
            }

            return fixed4(sum, 1.0);
        }

        ENDCG

        // 后处理“标配”
        ZTest Always
        Cull Off
        ZWrite Off

        Pass
        {
            // 为Pass定义名字，可以在其他Shader中直接通过这个名字使用该Pass，而无需再重复编写代码
            NAME "GAUSSIAN_BLUR_VERTICAL"

            CGPROGRAM

            // 使用代码块定义的函数作为顶点、片元着色函数
            #pragma vertex vertBlurVertical
            #pragma fragment fragBlur

            ENDCG
        }

        Pass
        {
            // 为Pass定义名字，可以在其他Shader中直接通过这个名字使用该Pass，而无需再重复编写代码
            NAME "GAUSSIAN_BLUR_HORIZONTAL"

            CGPROGRAM

            // 使用代码块定义的函数作为顶点、片元着色函数
            #pragma vertex vertBlurHorizontal
            #pragma fragment fragBlur

            ENDCG
        }
    }

    Fallback Off
}
